home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Game-Power
/
Amiga Game-Power.iso
/
pd mix ii
/
access
/
hddriver
/
driver
/
cache.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-20
|
10KB
|
516 lines
/*
* Copyright 1987 Alan Kent
*
* Permission is granted to redistribute this code as long
* as this message is retained in the code and the code is
* not sold without written permission from the author.
*
* UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk
* ACSnet: ajk@goanna.oz
* ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA
*/
#include "hd.h"
#define CACHE_SIZE 40
/* Cache Node types */
#define NOT_IN_CACHE 0
#define CN_SPECIAL 1
#define CN_HISTORY 2
#define CN_DIRTY 3
#define HASH_SIZE (CACHE_SIZE*3)
#define HASH(block) (((block)&0x7fff)%HASH_SIZE)
static struct cache * hash_table[ HASH_SIZE ];
static struct cache {
struct Node node;
struct cache *collision;
struct posn posn;
int type;
UBYTE buf[ HD_SECTOR ];
} cache[ CACHE_SIZE ];
#define DIRTY_LIMIT 10
#define SPECIAL_LIMIT ( CACHE_SIZE - DIRTY_LIMIT - 7 )
#define MAX_BASES 10
extern struct cache * cache_search ();
extern struct Node * RemHead ();
extern struct Node * RemTail ();
extern LONG log_to_phys ();
static struct List history;
static struct List dirty;
static int num_dirty;
static struct List special;
static int special_count;
static int num_bases = 0;
static LONG bases[ MAX_BASES ];
UBYTE *
read_cache ( posn )
struct posn *posn;
{
register struct cache *c;
int i;
struct posn next_posn;
/* see if sector is in special cache list */
c = cache_search ( posn );
if ( c != NULL ) {
switch ( c->type ) {
case CN_SPECIAL :
/* special list still good for a bit longer! */
special_count = 5;
Remove ( c );
AddHead ( &history , c );
c->type = CN_HISTORY;
return ( c->buf );
case CN_DIRTY :
/* yes, well have to leave it there! */
return ( c->buf );
case CN_HISTORY :
Remove ( c );
AddHead ( &history , c ); /* move to head of history */
c->type = CN_HISTORY;
return ( c->buf );
}
}
/* see if the special_case is really being used for anything */
if ( --special_count <= 0 ) {
/* move everything left in the special cache over to the normal */
/* cache - it does not seem to be needed anymore */
while ( ( c = (struct cache *)special.lh_Head )->node.ln_Succ != NULL ) {
Remove ( c );
AddTail ( &history , c );
c->type = CN_HISTORY;
}
}
/* ok, well we have to actually go and read the sector! */
/* if the special cache list is not empty, just read a sector */
if ( special.lh_Head->ln_Succ != NULL ) {
c = (struct cache *) RemTail ( &history );
new_posn ( c , posn );
flush_seek ( &c->posn );
read_sector ( &c->posn , c->buf );
analyse ( c , &next_posn );
AddHead ( &history , c );
c->type = CN_HISTORY;
return ( c->buf );
}
/* ok!! now, the special list is EMPTY! Is the disk access we want */
/* to do the NEXT of a recently accessed page? */
for ( i = 0, c = (struct cache *) history.lh_Head;
i < 5 && c->node.ln_Succ != NULL;
i++, c = (struct cache *)c->node.ln_Succ ) {
if ( is_next ( posn , c ) ) {
/* GOODY! Looks like we have got something to pre-read! */
/* first special entry will be what we asked for */
next_posn = *posn;
for ( i = 0; i < SPECIAL_LIMIT; i++ ) {
/* if in any of the caches, dont re-read! */
if ( cache_search ( &next_posn ) != NULL )
break;
c = (struct cache *) RemTail ( &history );
new_posn ( c , &next_posn );
flush_seek ( &c->posn );
read_sector ( &c->posn , c->buf );
analyse ( c , &next_posn );
AddTail ( &special , c );
c->type = CN_SPECIAL;
/* end of list will set -ve values */
if ( next_posn.cylinder < 0 )
break;
}
c = (struct cache *) special.lh_Head;
Remove ( c );
AddHead ( &history , c );
c->type = CN_HISTORY;
special_count = 5;
return ( c->buf );
}
}
/* Oh well, its just a boring read then */
c = (struct cache *) RemTail ( &history );
new_posn ( c , posn );
flush_seek ( &c->posn );
read_sector ( &c->posn , c->buf );
analyse ( c , &next_posn );
AddHead ( &history , c );
c->type = CN_HISTORY;
return ( c->buf );
}
void
write_cache ( posn , buf )
struct posn *posn;
char *buf;
{
register struct cache *c , *scan;
/* is sector already in a cache? */
c = cache_search ( posn );
if ( c != NULL ) {
if ( c->type == CN_DIRTY ) {
/* dirty sectors can be left were they are */
copy_sector ( buf , c->buf );
return;
}
/* otherwise, fall through - it will have to be put in the */
/* dirty list */
}
else {
/* get a cache for the sector. */
c = (struct cache *) history.lh_TailPred;
}
/* Ok, we have got a sector to put everything in */
Remove ( c );
new_posn ( c , posn );
copy_sector ( buf , c->buf );
/* now, we need a sorted insertion to keep dirty list sorted by cyl */
if ( dirty.lh_Head->ln_Succ != NULL ) {
for ( scan = (struct cache *)dirty.lh_Head;
scan->node.ln_Succ != NULL;
scan = (struct cache *) scan->node.ln_Succ ) {
if ( scan->posn.cylinder > posn->cylinder )
break;
}
Insert ( &dirty , c , scan->node.ln_Pred );
}
else { /* only node in list */
AddHead ( &dirty , c );
}
c->type = CN_DIRTY;
num_dirty++;
/* if too many dirty pages, probably should write some out */
if ( num_dirty > DIRTY_LIMIT ) {
/* try and find a close cylinder */
for ( scan = (struct cache *)dirty.lh_Head;
scan->node.ln_Succ != NULL;
scan = (struct cache *)scan->node.ln_Succ ) {
if ( scan->posn.cylinder >= cur_posn.cylinder )
break;
}
/* if at end of list, just use last entry */
if ( scan->node.ln_Succ == NULL )
scan = (struct cache *) scan->node.ln_Pred;
write_dirty ( scan );
}
}
void
flush_all ()
{
while ( dirty.lh_Head->ln_Succ != NULL )
write_dirty ( dirty.lh_Head );
}
write_dirty ( c )
register struct cache *c;
{
Remove ( c );
num_dirty--;
write_sector ( &c->posn , c->buf );
AddTail ( &history , c );
c->type = CN_HISTORY;
}
void
clear_all ()
{
register int i;
register struct cache *c;
NewList ( &special );
NewList ( &dirty );
NewList ( &history );
special_count = 0;
num_dirty = 0;
for ( i = 0; i < HASH_SIZE; i++ )
hash_table[i] = NULL;
for ( i = 0; i < CACHE_SIZE; i++ ) {
c = &cache[i];
c->posn.cylinder = -3;
c->posn.sector = 0;
c->posn.surface = 0;
c->posn.block = -3 * first.sectors * first.heads;
AddTail ( &history , c );
c->type = CN_HISTORY;
c->collision = hash_table[ HASH(c->posn.block) ];
hash_table[ HASH(c->posn.block) ] = c;
}
}
int
init_cache ()
{
register int i;
clear_all ();
return ( 0 );
}
void
free_cache ()
{
register int i;
flush_all ();
}
struct cache *
cache_search ( posn )
register struct posn *posn;
{
register struct cache *c;
c = hash_table[ HASH(posn->block) ];
while ( c != NULL ) {
if ( posn->block == c->posn.block )
return ( c );
c = c->collision;
}
return ( NULL );
}
new_posn ( c , posn )
register struct cache *c;
struct posn *posn;
{
register struct cache **old;
register struct cache **h;
/* first, remove from existing hash table position */
old = &hash_table[ HASH(c->posn.block) ];
while ( *old != NULL ) {
if ( *old == c ) { /* found pointer to c */
*old = (*old)->collision; /* unlink node */
break;
}
old = &(*old)->collision;
}
/* now update the position */
c->posn = *posn;
/* now insert into hash table at new position */
h = &hash_table[ HASH(c->posn.block) ];
c->collision = *h;
*h = c;
}
flush_seek ( posn ) /* write out all dirty tracks on way to posn */
struct posn *posn;
{
register struct cache *c;
if ( posn->cylinder > cur_posn.cylinder ) {
for ( c = (struct cache *)dirty.lh_Head;
c->node.ln_Succ != NULL;
c = (struct cache *)c->node.ln_Succ ) {
if ( c->posn.cylinder >= cur_posn.cylinder
&& c->posn.cylinder <= posn->cylinder ) {
c = (struct cache *)c->node.ln_Pred;
write_dirty ( c->node.ln_Succ );
}
}
}
else {
for ( c = (struct cache *)dirty.lh_TailPred;
c->node.ln_Pred != NULL;
c = (struct cache *)c->node.ln_Pred ) {
if ( c->posn.cylinder <= cur_posn.cylinder + 1
&& c->posn.cylinder >= posn->cylinder ) {
c = (struct cache *)c->node.ln_Succ;
write_dirty ( c->node.ln_Pred );
}
}
}
}
/* have a look, check block # to see if any new partitions */
analyse ( c , next_posn )
register struct cache *c;
register struct posn *next_posn;
{
LONG logical_block , base;
LONG type , subtype;
ULONG *buf;
buf = (ULONG*) c->buf;
type = buf[0];
subtype = buf[127];
if ( type == 2 && subtype == 2 /* File Header */
|| type == 2 && subtype == -3 ) { /* User Directory */
/* these blocks point to themselves, so we can use block # */
/* to work out where partitions start */
base = c->posn.block - buf[1];
/* see if we know about this base yet, if not add it */
new_base ( base );
}
/* ok, now look for a next pointer */
get_next ( c , next_posn );
}
get_next ( c , next_posn )
register struct cache *c;
register struct posn *next_posn;
{
LONG type , subtype , next , block;
ULONG *buf;
buf = (ULONG *) c->buf;
type = buf[0];
subtype = buf[127];
next = buf[4];
next_posn->block = -1;
next_posn->sector = -1;
next_posn->surface = -1;
next_posn->cylinder = -1;
if ( type == 2 && subtype == -3 /* file header */
|| type == 16 && subtype == -3 /* file extension */
|| type == 8 ) { /* data block */
block = log_to_phys ( c->posn.block , next );
if ( block >= 0
&& block < first.sectors * first.heads * first.cylinders ) {
next_posn->block = block;
next_posn->sector = block % first.sectors;
next_posn->surface = ( block / first.sectors ) % first.heads;
next_posn->cylinder = block / ( first.sectors * first.heads );
}
}
}
LONG
log_to_phys ( real , logical )
LONG real , logical;
{
register int i;
for ( i = num_bases - 1; i >= 0; i-- )
if ( real >= bases[i] )
return ( logical + bases[i] );
return ( -1 );
}
new_base ( base )
LONG base;
{
register int i , j;
for ( i = num_bases - 1; i >= 0; i-- ) {
if ( bases[i] == base )
return;
if ( bases[i] < base )
break;
}
i++;
for ( j = num_bases; j > i; j-- )
bases[j] = bases[j-1];
bases[i] = base;
num_bases++;
}
is_next ( posn , c )
struct posn *posn;
struct cache *c;
{
struct posn next_posn;
get_next ( c , &next_posn );
return ( posn->block == next_posn.block );
}